package org.strategies.dao;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.core.io.Resource;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by DevMode on 06.01.14.
 */
public class QueryProvider {

    private static final Logger log = Logger.getLogger(QueryProvider.class);

    /** Regexp паттерн для парсинга DAO файла с SQL запросами */
    private static final Pattern REGEXP =
        Pattern.compile("#\\s*(\\w+)\\s*:\\s*\\{\\s*([^#]+)\\s*\\}", Pattern.DOTALL);

    /** Коллекция запросов, где ключом является имя DAO файла, а значением коллекция именованных SQL запросов */
    private final Map<String, Map<String, String>> queryMapping =
        new ConcurrentHashMap<String, Map<String, String>>();

    /** Коллекция, в которой хранится время последней модификации каждого DAO файла */
    private final Map<String, Long> daoFilesLastModified = new HashMap<String, Long>();

    /** Массив DAO файлов с SQL запросами */
    private final Resource[] locations;

    /**
     * Конструктор
     * @param locations список DAO файлов с SQL запросами
     */
    public QueryProvider(final Resource[] locations) {
        this.locations = locations;
    }

    /**
     * Получить текст SQL запроса по имени DAO файла и имени запроса.
     * @param sqlDaoFileName имя DAO файла с запросами
     * @param queryName имя запроса
     * @return текст SQL запроса
     */
    public String getQuery(final String sqlDaoFileName, final String queryName) {
        return queryMapping.get(sqlDaoFileName).get(queryName);
    }

    /**
     * Распарсить указанный DAO файл в коллекцию поименованных запросов.
     * @param res DAO файл с запросами
     * @return коллекция поименованных запросов
     * @throws java.io.IOException ошибка чтения файлов
     */
    private Map<String, String> parseDaoFile(final Resource res) throws IOException {

        final Map<String, String> result = new HashMap<String, String>();
        final String content = IOUtils.toString(res.getInputStream());
        final Matcher m = REGEXP.matcher(content);
        while (m.find()) {
            final String queryName = m.group(1);
            final String query = m.group(2).replaceAll("\\s+", " ");
            result.put(queryName, query);
        }
        return result;
    }

    /**
     * Обновить запросы в памяти, если поменялись соответствующие DAO файлы.
     */
    @PostConstruct()
    public void loadQueries() {

        try {
            for (Resource res : locations) {
                final String daoFilename = res.getFilename();
                final Long lastModified = res.lastModified();
                if (!lastModified.equals(daoFilesLastModified.get(daoFilename))) {
                    daoFilesLastModified.put(daoFilename, lastModified);
                    queryMapping.put(daoFilename, parseDaoFile(res));
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }
}
